﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dokan;
using MediaAssistant.DAL;

namespace MediaFS
{
    class MediaFSOperations : IMediaFSOperations
    {
        private int _count;
        private Dictionary<string, string> FilePathMap { get; set; }
        private Dictionary<string, LibraryItem> LibraryItemPathMap { get; set; }
        public MediaFSOperations()
        {
            _count = -1;
            FilePathMap = new Dictionary<string, string>();
            LibraryItemPathMap = new Dictionary<string, LibraryItem>();
        }
        public int Cleanup(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CloseFile(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int CreateDirectory(string filename, DokanFileInfo info)
        {
            return -1;
        }

        public int CreateFile(string filename, FileAccess access, FileShare share, FileMode mode, FileOptions options, DokanFileInfo info)
        {
            info.Context = _count++;
            var path = GetPath(filename);
            if (string.IsNullOrEmpty(path))
                return -1;
            if (File.Exists(path))
            {
                return 0;
            }
            info.IsDirectory = true;
            return 0;
        }

        public int DeleteDirectory(string filename, DokanFileInfo info)
        {
            return -1;
        }

        public int DeleteFile(string filename, DokanFileInfo info)
        {
            return -1;
        }

        public int FlushFileBuffers(string filename, DokanFileInfo info)
        {
            return -1;
        }

        public int FindFiles(string filename, ArrayList files, DokanFileInfo info)
        {
            lock (DatabaseManager.LockObject)
            {
                if (filename == "\\")
                {
                    foreach (var item in DatabaseManager.Instance.GetRootLibraryItems())
                    {
                        AddDirectoryInfo(item.Title, files);
                    }
                    return 0;
                }
                var libraryItem = GetLibraryItem(filename);
                AddFileInfo(libraryItem, files);
            }
            return 0;
        }

        private static void AddFileInfo(LibraryItem item, ArrayList files)
        {
            lock (DatabaseManager.LockObject)
            {
                foreach (var libraryItem in item.Children)
                {
                    AddDirectoryInfo(libraryItem.Title, files);
                }
                AddMovieFiles(files, item.Movies);
                foreach (var music in item.Musics)
                {
                    if(string.IsNullOrEmpty(music.FullPath)==false && File.Exists(music.FullPath))
                        AddFileInfo(music.FileName, files);
                }
                //var selectedProfile = MediaFSManager.Instance.SelectedProfile;
                //switch (item.Type)
                //{
                //    case LibraryItemType.ProcessingLibrary:
                //        AddMovieFiles(files, DatabaseManager.Instance.UpdateProcessingMovies());
                //        break;
                //    case LibraryItemType.FailedLibrary:
                //        AddMovieFiles(files, DatabaseManager.Instance.GetFailedMovies());
                //        break;
                //    case LibraryItemType.UnreadMovieLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetNewMovies(selectedProfile));
                //        }
                //        break;
                //    case LibraryItemType.WatchListLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetWatchedMovies(selectedProfile));
                //        }
                //        break;
                //    case LibraryItemType.WishListLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetWishListMovie(selectedProfile));
                //        }
                //        break;
                //    case LibraryItemType.StaredMovieLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetStarMovies(selectedProfile));
                //        }
                //        break;
                //    case LibraryItemType.RecommendedMovieLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetStarMovies(selectedProfile));
                //        }
                //        break;
                //    case LibraryItemType.RecentlyPlayedMovieLibrary:
                //        if (selectedProfile != null)
                //        {
                //            AddMovieFiles(files, DatabaseManager.Instance.GetRecentlyPlayedMovies(selectedProfile));
                //        }
                //        break;
                //}
            }
        }

        private static void AddMovieFiles(ArrayList files, IEnumerable<Movie> movies)
        {
            foreach (var movie in movies)
            {
                var fileToPlay = movie.GetFileToPlay();
                if(string.IsNullOrEmpty(fileToPlay)==false && File.Exists(fileToPlay))
                    AddFileInfo(fileToPlay, files);
            }
        }

        private static void AddFileInfo(string fileName, ArrayList files)
        {
            var fileInfo = new FileInfo(fileName);
            var finfo = new FileInformation
            {
                FileName = fileInfo.Name,
                Attributes = fileInfo.Attributes,
                LastAccessTime = fileInfo.LastAccessTime,
                LastWriteTime = fileInfo.LastWriteTime,
                CreationTime = fileInfo.CreationTime,
                Length = fileInfo.Length
            };
            files.Add(finfo);
        }
        private static void AddDirectoryInfo(string fileName, ArrayList files)
        {
            var finfo = new FileInformation
            {
                FileName = fileName,
                Attributes = FileAttributes.Directory,
                LastAccessTime = DateTime.Now,
                LastWriteTime = DateTime.Now,
                CreationTime = DateTime.Now,
                Length = 0
            };
            files.Add(finfo);
        }

        private LibraryItem GetLibraryItem(string filename)
        {
            if (LibraryItemPathMap.ContainsKey(filename))
            {
                return LibraryItemPathMap[filename];
            }
            var tokens = filename.Split("\\".ToCharArray()).Where(t => string.IsNullOrWhiteSpace(t) == false);
            var items = DatabaseManager.Instance.GetRootLibraryItems();
            var item = items.FirstOrDefault();
            foreach (var token in tokens)
            {
                string title = token;
                var temp = items.FirstOrDefault(o => o.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase));
                if (temp == null)
                    break;
                item = temp;
                items = item.Children;
            }
            if (item != null)
            {
                LibraryItemPathMap.Add(filename, item);
            }
            return item;
        }


        public int GetFileInformation(string filename, FileInformation fileinfo, DokanFileInfo info)
        {
            if (filename == "\\")
            {
                fileinfo.Attributes = FileAttributes.Directory;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;
                return 0;
            }
            var path = GetPath(filename);
            if (string.IsNullOrEmpty(path) == false)
            {
                var actualFileInfo = new FileInfo(path);
                fileinfo.Attributes = actualFileInfo.Attributes;
                fileinfo.LastAccessTime = actualFileInfo.LastAccessTime;
                fileinfo.LastWriteTime = actualFileInfo.LastWriteTime;
                fileinfo.CreationTime = actualFileInfo.CreationTime;
                fileinfo.Length = actualFileInfo.Exists ? actualFileInfo.Length : 1024;
                fileinfo.FileName = actualFileInfo.Name;
                return 0;
            }
            if (path == filename)
            {
                fileinfo.Attributes = FileAttributes.Directory;
                fileinfo.LastAccessTime = DateTime.Now;
                fileinfo.LastWriteTime = DateTime.Now;
                fileinfo.CreationTime = DateTime.Now;
                return 0;
            }
            return -1;
        }

        public int LockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return 0;
        }

        public int MoveFile(string filename, string newname, bool replace, DokanFileInfo info)
        {
            return -1;
        }

        public int OpenDirectory(string filename, DokanFileInfo info)
        {
            return 0;
        }

        public int ReadFile(string filename, byte[] buffer, ref uint readBytes, long offset, DokanFileInfo info)
        {
            try
            {
                var path = GetPath(filename);
                if (string.IsNullOrEmpty(path))
                    return -1;
                var fs = File.OpenRead(path);
                fs.Seek(offset, SeekOrigin.Begin);
                readBytes = (uint)fs.Read(buffer, 0, buffer.Length);
                return 0;
            }
            catch (Exception)
            {
                return -1;
            }
        }

        private string GetPath(string filename)
        {
            if (FilePathMap.ContainsKey(filename) == false)
            {
                lock (DatabaseManager.LockObject)
                {
                    if (FilePathMap.ContainsKey(filename) == false)
                    {
                        var fileInfo = new FileInfo(filename);
                        var libraryItem = GetLibraryItem(filename);
                        if (Utility.IsMusicFile(filename))
                        {
                            var music = libraryItem.Musics.FirstOrDefault(m => IsSameFile(fileInfo, m.FileName));
                            FilePathMap.Add(filename, music.FullPath);
                        }
                        else if (Utility.IsMovieFile(filename))
                        {
                            var movie = libraryItem.Movies.FirstOrDefault(m => IsSameFile(fileInfo, m.FileName));
                            FilePathMap.Add(filename, movie.FullPath);
                        }
                        else if (libraryItem != null)
                            FilePathMap.Add(filename, filename);
                    }
                }
            }
            return FilePathMap.ContainsKey(filename) ? FilePathMap[filename] : string.Empty;
        }

        private static bool IsSameFile(FileInfo fileInfo, string fileName)
        {
            return fileName!=null && fileName.Equals(fileInfo.Name, StringComparison.InvariantCultureIgnoreCase);
        }

        public int SetEndOfFile(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetAllocationSize(string filename, long length, DokanFileInfo info)
        {
            return -1;
        }

        public int SetFileAttributes(string filename, FileAttributes attr, DokanFileInfo info)
        {
            return -1;
        }

        public int SetFileTime(string filename, DateTime ctime, DateTime atime, DateTime mtime, DokanFileInfo info)
        {
            return -1;
        }

        public int UnlockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return 0;
        }

        public int Unmount(DokanFileInfo info)
        {
            return 0;
        }

        public void Initialize(IWaitScreenService waitScreenService)
        {
            
        }

        public int GetDiskFreeSpace(ref ulong freeBytesAvailable, ref ulong totalBytes, ref ulong totalFreeBytes, DokanFileInfo info)
        {
            freeBytesAvailable = 512 * 1024 * 1024;
            totalBytes = 1024 * 1024 * 1024;
            totalFreeBytes = 512 * 1024 * 1024;
            return 0;
        }

        public int WriteFile(string filename, byte[] buffer, ref uint writtenBytes, long offset, DokanFileInfo info)
        {
            return -1;
        }
    }
}
